using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using WPCoco;

namespace FluidDemo.Model
{
    class GridListQuery : ParticleLocationQuery
    {
        public GridListQuery(Vector2 mapSize, Size gridSize)
        {
            GridSize = gridSize;
            MapSize = mapSize;
            CellSize = new Vector2(mapSize.X / gridSize.Width, mapSize.Y / gridSize.Height);
            CreateStructure();
        }

        public readonly Vector2 MapSize;
        public readonly Size GridSize;
        public readonly Vector2 CellSize;

        List<Particle>[,] grid = null;

        void CreateStructure() 
        {
            grid = new List<Particle>[GridSize.Height, GridSize.Width];
            for (int i = 0; i < GridSize.Height; i++)
                for (int j = 0; j < GridSize.Width; j++)
                    grid[i, j] = new List<Particle>(5);
        }

        void ResetStructure() 
        {
            for (int i = 0; i < GridSize.Height; i++)
                for (int j = 0; j < GridSize.Width; j++)
                    grid[i, j].Clear();
        }

        Point GetCellForPoint(Vector2 position) 
        {
            position.X = MathHelper.Clamp(position.X, 0, MapSize.X - 1);
            position.Y = MathHelper.Clamp(position.Y, 0, MapSize.Y - 1);
            return new Point((int)(position.X / CellSize.X), (int)(position.Y / CellSize.Y));
        }

        public override void Update(IList<Particle> actors)
        {
            ResetStructure();
            for (int i = 0; i < actors.Count; i++)
            {
                var a = actors[i];
                Point p = GetCellForPoint(a.Position);
                grid[p.Y, p.X].Add(a);
            }
        }

        public override void GetParticlesNear(Vector2 position, Vector2 size, ParticleCallback callback)
        {
            size *= 0.5f;
            int w = (int)(size.X / CellSize.X + 0.5f);
            if (w == 0) w = 1;
            int h = (int)(size.Y / CellSize.Y + 0.5f);
            if (h == 0) h = 1;
            Point p = GetCellForPoint(position);
            int f0 = Math.Max(p.Y - h, 0);
            int c0 = Math.Max(p.X - w, 0);
            int f1 = Math.Min(p.Y + h, GridSize.Height - 1);
            int c1 = Math.Min(p.X + w, GridSize.Width - 1);
            List<Particle> list;
            for (int i = f0; i <= f1; i++)
            {
                for (int j = c0; j <= c1; j++)
                {
                    list = grid[i, j];
                    if (list.Count == 0) continue;
                    for (int k = 0; k < list.Count; k++)
                        callback(list[k]);
                }
            }
        }

        public override int ResolveCollitions(Vector2 position, Vector2 size, Particle pa) 
        {
            size *= 0.5f;
            int temp = 0;
            int w = (int)(size.X / CellSize.X + 0.5f);
            if (w == 0) w = 1;
            int h = (int)(size.Y / CellSize.Y + 0.5f);
            if (h == 0) h = 1;
            Point p = GetCellForPoint(position);
            int f0 = Math.Max(p.Y - h, 0);
            int c0 = Math.Max(p.X - w, 0);
            int f1 = Math.Min(p.Y + h, GridSize.Height - 1);
            int c1 = Math.Min(p.X + w, GridSize.Width - 1);
            List<Particle> list;
            for (int i = f0; i <= f1; i++)
            {
                for (int j = c0; j <= c1; j++)
                {
                    list = grid[i, j];
                    if (list.Count == 0) continue;
                    for (int k = 0; k < list.Count; k++)
                    {
                        Particle pb = list[k];
                        if (pa == pb) continue;
                        bool check = CollitionResolver.Solve(pa, pb);
                        temp += check ? 1 : 0;
                    }
                }
            }
            return temp;
        }
    }
}
